<?php
/**
 * User: xialei
 * Date: 2016/6/28 0028
 * Time: 11:52
 */

namespace app\components;


use Requests;
use yii\base\InvalidConfigException;
use yii\base\Object;
use yii\base\UserException;
use yii\helpers\Json;
use yii\web\HttpException;

class Wechat extends Object
{
    public $ak;
    public $sk;
    public $accessToken;
    private $cacheKey;

    private $jsTicketKey;
    public $jsTicket;

    static $baseURL = 'https://api.weixin.qq.com/cgi-bin';

    public function init()
    {
        parent::init();
        if (empty($this->ak) || empty($this->sk)) {
            throw new InvalidConfigException('参数错误');
        }
        $this->cacheKey = $this->ak . '-accesstoken';
        $this->jsTicketKey = $this->ak . '-jsTicketKey';
    }


    /**
     * 获取accessToken
     * @return mixed
     * @throws HttpException
     * @throws UserException
     */
    public function getAccessToken()
    {
        if (!empty($this->accessToken)) {
            return $this->accessToken;
        }
        $data = \Yii::$app->cache->get($this->cacheKey);
        if (!empty($data)) {
            $this->accessToken = $data;
            return $data;
        }

        $params = [
            'grant_type' => 'client_credential',
            'appid' => $this->ak,
            'secret' => $this->sk
        ];

        $resp = Requests::get(self::$baseURL . '/token?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        \Yii::$app->cache->set($this->cacheKey, $data['access_token'], $data['expires_in'] - 100);
        $this->accessToken = $data['access_token'];
        return $data['access_token'];
    }

    private function handleException($data)
    {
        if (isset($data['errcode']) && $data['errcode'] > 0) {
            throw new UserException($data['errmsg'], $data['errcode']);
        }
    }

    /**
     * @return mixed
     */
    public function getAk()
    {
        return $this->ak;
    }

    /**
     * 获取授权链接
     * @param $callback
     * @param string $scope
     * @param string $state
     * @return string
     */
    public function buildOauthUrl($callback, $state = '123456', $scope = 'snsapi_userinfo')
    {
        $params = [
            'appid' => $this->ak,
            'redirect_uri' => $callback,
            'response_type' => 'code',
            'scope' => $scope,
            'state' => $state
        ];
        return 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
    }

    /**
     * 获取授权token
     * @param $code
     * @return mixed
     * @throws HttpException
     * @throws UserException
     */
    public function getOauth2AccessToken($code)
    {
        $params = [
            'appid' => $this->ak,
            'secret' => $this->sk,
            'code' => $code,
            'grant_type' => 'authorization_code'
        ];
        $resp = Requests::get('https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 获取用户信息
     * @param $accessToken
     * @param $openid
     * @param string $lang
     * @return mixed
     * @throws HttpException
     * @throws UserException
     */
    public function getOauth2UserInfo($accessToken, $openid, $lang = 'zh_CN')
    {
        $params = [
            'access_token' => $accessToken,
            'openid' => $openid,
            'lang' => $lang
        ];

        $resp = Requests::get('https://api.weixin.qq.com/sns/userinfo?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 自定义菜单
     * @return mixed
     * @throws HttpException
     */
    public function getMenu()
    {
        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::get('https://api.weixin.qq.com/cgi-bin/menu/get?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }


    /**
     * 创建菜单
     * @param array $data
     * @return array|mixed
     * @throws HttpException
     */
    public function createMenu(array &$data)
    {
        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::post('https://api.weixin.qq.com/cgi-bin/menu/create?' . http_build_query($params), [], json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 获取客服列表
     * @return mixed
     * @throws HttpException
     */
    public function getKflist()
    {
        $url = 'https://api.weixin.qq.com/cgi-bin/customservice/getkflist';
        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::get($url . '?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 添加客服
     * @param $account
     * @param $nickname
     * @param $password
     * @return array|mixed
     * @throws HttpException
     */
    public function addKf($account, $nickname, $password)
    {
        $data = [
            'kf_account' => $account,
            'nickname' => $nickname,
            'password' => md5($password)
        ];

        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::post('https://api.weixin.qq.com/customservice/kfaccount/add?' . http_build_query($params), [], json_encode($data, JSON_UNESCAPED_UNICODE));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 删除自定义菜单
     * @return mixed
     * @throws HttpException
     * @throws UserException
     */
    public function deleteMenu()
    {
        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::get('https://api.weixin.qq.com/cgi-bin/menu/delete?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 当前菜单
     * @return mixed
     * @throws HttpException
     * @throws UserException
     */
    public function currentMenu()
    {
        $params = [
            'access_token' => $this->getAccessToken()
        ];
        $resp = Requests::get('https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        return $data;
    }

    /**
     * 获取JsTicket
     * @return mixed
     * @throws HttpException
     */
    public function getJsTicket()
    {
        if (!empty($this->jsTicket)) {
            return $this->jsTicket;
        }

        $resp = Requests::get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' . $this->getAccessToken() . '&type=jsapi');
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        $data = Json::decode($resp->body);
        $this->handleException($data);
        \Yii::$app->cache->set($this->jsTicketKey, $data['ticket'], $data['expires_in']);
        return $data['ticket'];
    }

    /**
     * 获取JsApiSign
     * @param $url
     * @param array $apis
     * @param bool $debug
     * @return string
     */
    public function getJsApiSign($url, array $apis, $debug = false)
    {
        $nonceStr = \Yii::$app->security->generateRandomString(8);
        $ticket = $this->getJsTicket();
        $timestamp = time();
        $params = [
            'noncestr' => $nonceStr,
            'jsapi_ticket' => $ticket,
            'timestamp' => $timestamp,
            'url' => $url
        ];
        ksort($params, SORT_STRING | SORT_ASC);
        $signStr = urldecode(http_build_query($params));
        $sign = sha1($signStr);

        return [
            'debug' => $debug,
            'appId' => $this->ak,
            'timestamp' => $timestamp,
            'nonceStr' => $nonceStr,
            'signature' => $sign,
            'jsApiList' => $apis
        ];
    }

    /**
     * 下载远程文件
     * @param $mediaId
     * @return mixed
     * @throws HttpException
     */
    public function downloadMedia($mediaId)
    {
        $params = [
            'access_token' => $this->getAccessToken(),
            'media_id' => $mediaId
        ];

        $resp = Requests::get('http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params));
        if ($resp->status_code != 200) {
            throw new HttpException('请求失败', $resp->status_code);
        }
        return $resp->body;
    }
}